// opencv async detector demo
// Author: lihaoweicv@hotmail.com
// MIT License


#ifndef CV_ASYNC_DEMO_RUNNER_H
#define CV_ASYNC_DEMO_RUNNER_H

#include "base/status.h"
#include "detector/abstract_detector.h"

#include <thread>

namespace cvf
{
    typedef  enum _RunnerEvent
    {
        NONE,
        NORMAL_RUNNING,
        STOP_LOOP
    } RunnerEvent;


    /**
     * @brief 检测子任务
     * 比如检测关键帧，或者拍摄的图像
     * 主任务是帧速率自适应的，会丢弃一些帧
     * 这个子任务用于补充上述的缺点
     */
    class Runner_Sub_Detect_Job
    {
    public:
        Runner_Sub_Detect_Job()
        {
            _callbackDone = false;

        }
        cv::Mat imgBGR; ///< in
        DetInfo detInfo;///< out
        virtual void callback(DetInfo &det)
        {
            // callback 中不能做耗时任务, 是同步回调
            detInfo = det;
            set_callback_done(true);
        }
        Status wait_and_get_dets(DetInfo &dets)
        {
            //TODO 等待超时逻辑
            LOGI("wait_and_get_dets...\n");
            while (!callback_done())
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
            }
            dets = detInfo;
            LOGI("get_dets finish!\n");
            return Status_OK;
        }

    private:
        bool  callback_done()
        {
            std::unique_lock<std::mutex> ulk(_mu);
            return  _callbackDone;
        }
        void set_callback_done(bool  v)
        {
            std::unique_lock<std::mutex> ulk(_mu);
            _callbackDone = true;
        }
        bool _callbackDone;
        std::mutex _mu;
    };

    class Async_Preview_Runner
    {
    public:
        Async_Preview_Runner();
        ~Async_Preview_Runner();
        void set_max_fps(int fps){ _maxFPS = fps; }

        void  bind_detector(std::shared_ptr<Abstract_Detector> & detector);

        Status init_models(std::vector<std::string> &modelPaths, bool  isAsync=false);

        Status start();

        Status push_frame(cv::Mat &frameBGR);

        Status fetch_dets(DetInfo &dets);

        Status stop();

        Status submit_once_sub_detect_job(Runner_Sub_Detect_Job *job);

    private:
        bool frame_ready()
        {
            std::unique_lock<std::mutex> ulk(_muFrame);
            return  _frameReady;
        }
        void set_frame_ready(bool v)
        {
            std::unique_lock<std::mutex> ulk(_muFrame);
            _frameReady = v;
        }

        void set_event(RunnerEvent e)
        {
            std::unique_lock<std::mutex> ulk(_mutEvent);
            _event = e;
        }
        RunnerEvent get_event()
        {
            std::unique_lock<std::mutex> ulk(_mutEvent);
            return  _event;
        }
        DetInfo get_dets()
        {
            std::unique_lock<std::mutex> ulk(_muDet);
            return  _dets;
        }
        void set_dets(DetInfo &d)
        {
            std::unique_lock<std::mutex> ulk(_muDet);
            _dets = d;
        }
        void get_frame(cv::Mat &ref, bool withCopy = false)
        {
            std::unique_lock<std::mutex> ulk(_muFrame);
            if(withCopy)
            {
                ref = _frame.clone();
            }
            else
            {
                ref = _frame;
            }
        }
        void  set_frame(cv::Mat &frame)
        {
            std::unique_lock<std::mutex> ulk(_muFrame);
            _frame = frame.clone();
            _frameReady = true;
        }
        bool do_sub_job()
        {
            std::unique_lock<std::mutex> ulk(_muSubJob);
            return _doSubJob;
        }
        void  set_do_sub_job(bool  v)
        {
            std::unique_lock<std::mutex> ulk(_muSubJob);
            _doSubJob = v;
        }
        bool is_running()
        {
            std::unique_lock<std::mutex> ulk(_muIsRun);
            return _isRunning;
        }
        void set_is_running(bool v)
        {
            std::unique_lock<std::mutex> ulk(_muIsRun);
            _isRunning = v;
        }

        void init_models_thread_fn();
        void do_sub_detect_job_fn();
        void thread_loop();


        int _maxFPS;
        std::mutex _muDet, _muFrame, _mutEvent, _muSubJob, _muIsRun;
        RunnerEvent _event;
        cv::Mat _frame;
        bool _frameReady;
        bool _doSubJob;
        bool _isRunning;
        bool _modelInited;
        DetInfo _dets;
        Runner_Sub_Detect_Job * _subJob;
        std::shared_ptr<Abstract_Detector>  _detector;
        std::vector<std::string> _modelPaths;
    };
}


#endif //CV_ASYNC_DEMO_RUNNER_H
